import tkinter
import time
import os
import pandas as pd
from docx import Document
from tkinter.messagebox import showerror,showinfo,showwarning
from tkinter.ttk import Progressbar
from tkinter import filedialog
from ttkbootstrap import Style
from PIL import Image, ImageTk
from make_docx import create_temp, make_temp_docx, combine_results

right_path = __file__.rstrip(os.path.basename(__file__))    # 获取当前文件的所在路径
os.chdir(right_path)  

root = tkinter.Tk()
root.title("与会证件合成器 V1.2")
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
min_width = int(screen_width * 0.3646)
min_height = int(screen_height * 0.37)
max_width = int(screen_width * 0.625)
max_height = int(screen_height * 0.635)
root.minsize(min_width, min_height)   # 初始化窗口大小
root.maxsize(max_width, max_height)   # 放大之后窗口大小

style = Style('minty')

# 校徽尺寸变化展示 #
def resize(w, h, w_box, h_box, pil_image):  
    ''' 
    resize a pil_image object so it will fit into 
    a box of size w_box times h_box, but retain aspect ratio 
    对一个pil_image对象进行缩放，让它在一个矩形框内，还能保持比例 
    '''  
    f1 = w_box/w # 1.0 forces float division in Python2  
    f2 = h_box/h  
    factor = min([f1, f2])  
    #print(f1, f2, factor) # test  
    # use best down-sizing filter  
    width = int(w*factor)  
    height = int(h*factor)  
    return pil_image.resize((width, height), Image.LANCZOS)  # 在10.0.0后版本的pillow，ANTIALIAS须更名为LANCZOS，算法完全一致

w_box = 75 # 图片期望大小
h_box = 75

pil_image = Image.open('SHS_transparent.png') # 打开图片
w, h = pil_image.size  # 获取图像的原始大小  
pil_image_resized = resize(w, h, w_box, h_box, pil_image) # 缩放图像让它保持比例，同时限制在一个矩形框范围内
tk_image = ImageTk.PhotoImage(pil_image_resized) # 把PIL图像对象转变为Tkinter的PhotoImage对象
# 绑定校徽展示框 #
label_logo = tkinter.Label(root, image=tk_image, width=w_box, height=h_box)
label_logo.place(relx=0.01, rely=0.01)
# 大标题 #
label_title = tkinter.Label(root,text='上海中学  学代会/团代会证件合成器',font=('微软雅黑',20,'bold'))
label_title.place(relx=0.15, rely=0.01)

# 定义文件选择按钮功能 #
def select_file_model():
    entry_model.delete(0,'end')
    selected_file_path = filedialog.askopenfilename(title='选择模版文件', filetypes=(('Word文件(docx)','*.docx'),('全部文件','*.*')))
    entry_model.insert(10, selected_file_path)

def select_file_data():
    entry_data.delete(0,'end')
    selected_file_path = filedialog.askopenfilename(title='选择数据源', filetypes=(('逗号分隔符文件(csv)','*.csv'),('Excel工作表(xlsx)','*.xlsx'),('全部文件','*.*')))
    entry_data.insert(10, selected_file_path)

def select_folder_image():
    entry_image.delete(0,'end')
    selected_folder_path = filedialog.askdirectory(title='选择图片位置')
    entry_image.insert(10, selected_folder_path)

def select_folder_target():
    entry_target.delete(0,'end')
    selected_folder_path = filedialog.askdirectory(title='选择目标位置')
    entry_target.insert(10, selected_folder_path)


# 创建输入框 #
label_model = tkinter.Label(root, text='选择合成模板(docx)：')
label_model.place(relx=0.075, rely=0.2)
entry_model = tkinter.Entry(root)
entry_model.place(relx=0.32, rely=0.2, width=200, height=20)
button_select_model = tkinter.Button(root, text='选择模板', command=select_file_model)
button_select_model.place(relx=0.62, rely=0.2, height=20)

label_data = tkinter.Label(root, text='选择身份数据源(csv/xlsx)：')
label_data.place(relx=0.075,rely=0.3)
entry_data = tkinter.Entry(root)
entry_data.place(relx=0.32, rely=0.3, width=200, height=20)
button_select_data = tkinter.Button(root, text='选择数据', command=select_file_data)
button_select_data.place(relx=0.62, rely=0.3, height=20)

label_image = tkinter.Label(root, text='选择图片(证件照)文件夹：')
label_image.place(relx=0.075,rely=0.4)
entry_image = tkinter.Entry(root)
entry_image.place(relx=0.32, rely=0.4, width=200, height=20)
button_select_img = tkinter.Button(root, text='选择文件夹', command=select_folder_image)
button_select_img.place(relx=0.62, rely=0.4, height=20)

label_target = tkinter.Label(root, text='选择输出文件目标位置：')
label_target.place(relx=0.075,rely=0.5)
entry_target = tkinter.Entry(root)
entry_target.place(relx=0.32, rely=0.5, width=200, height=20)
button_select_img = tkinter.Button(root, text='选择位置', command=select_folder_target)
button_select_img.place(relx=0.62, rely=0.5, height=20)

label_name = tkinter.Label(root, text='输出文件文件名：')
label_name.place(relx=0.075,rely=0.6)
entry_name = tkinter.Entry(root)
entry_name.place(relx=0.32, rely=0.6, width=200, height=20)
entry_name.insert(10,'result')

label_progress = tkinter.Label(root, text='当前进度：无     ')
label_progress.place(relx=0.78,rely=0.695)

# 一键清空已输入内容函数 #
def clean_up_entry():
    entry_data.delete(0,'end')
    entry_model.delete(0,'end')
    entry_image.delete(0,'end')
    entry_target.delete(0,'end')
    entry_name.delete(0,'end')
    entry_name.insert(10,'result')
# 清空按钮 #
button_cleanup = tkinter.Button(root, text='恢复默认', command=clean_up_entry)
button_cleanup.place(relx=0.075, rely=0.7, height=20)

# 进度条 #
progressbar = Progressbar(root, mode='determinate', maximum=100, value=0, length=350, orient='horizontal')
progressbar.place(relx=0.27, rely=0.71)

# 空白检验 #
def is_null(s):
    try:    # 如果s字符串的长度是0， 返回True（s是空白字符串）
        if len(s) == 0:
            return True
    except (TypeError, ValueError): 
        pass # 如果引发value error，不做任何事情
    try:
        if s.isspace() == True: # 如果能运行isspace()函数，即字符串长度不为零但由空格字符组成，返回True
            return True
        if s.isspace() == False:
            return False
    except (TypeError, ValueError):
        return False

# 合成功能函数 #
def result_out():
    progressbar['value'] = 0
    # 定义传参变量 #
    model = entry_model.get()
    data = entry_data.get()
    image_folder = entry_image.get()
    target_path = entry_target.get()
    file_name = target_path + '//' + entry_name.get() + '.docx'
    if is_null(model) or is_null(data) or is_null(image_folder) or is_null(target_path):
        # print(model, data, image_folder, target_path)
        showerror(title='错误！', message='请检查并确认所有输入项都已填入！')
    elif data.split('.')[len(data.split('.'))-1] != 'csv' and data.split('.')[len(data.split('.'))-1] != 'xlsx':
        showerror(title='错误！', message='请检查并确认身份数据源是csv或xlsx格式！')
    else:
        if data.split('.')[len(data.split('.'))-1] == 'csv':
            df = pd.read_csv(data)
        else:
            df = pd.read_excel(data)
        docx = Document(model)
        # df = pd.read_csv(data)
        name = df["姓名"].tolist()
        Class = df['班级'].tolist()
        pic_path = df['图片'].tolist()
        result_path_list = []
        result_path_list = create_temp(docx, name)
        
        label_progress = tkinter.Label(root, text='当前进度：初始化序列')
        label_progress.place(relx=0.78,rely=0.695)
        for i in range(1, len(result_path_list)+1):
            progressbar['value'] = 10 * (i / len(result_path_list))
            root.update()
            time.sleep(0.01)
        
        label_progress = tkinter.Label(root, text='当前进度：合成临时文件')
        label_progress.place(relx=0.78,rely=0.695)
        make_temp_docx(result_path_list, name, Class, pic_path, image_folder)
        for i in range(1, len(result_path_list)+1):
            progressbar['value'] = 10 + 60 * (i / len(result_path_list))
            root.update()
            time.sleep(0.01)
        
        label_progress = tkinter.Label(root, text='当前进度：清除缓存       ')
        label_progress.place(relx=0.78,rely=0.695)
        combine_results(result_path_list, file_name)
        for i in range(1, len(result_path_list)+1):
            progressbar['value'] = 70 + 30 * (i / len(result_path_list))
            root.update()
            time.sleep(0.01)
        showwarning(title='制作完成！', message='请打开合成后文档检查格式和内容是否正确！')
        label_progress = tkinter.Label(root, text='当前进度：无           ')
        progressbar['value'] = 0
        label_progress.place(relx=0.78,rely=0.695)

# 开始合成按钮 #
button_result_out = tkinter.Button(root, text='开始合成', command=result_out)
button_result_out.place(relx=0.17, rely=0.7, height=20)
# 菜单栏 #
def show_info():
    showinfo(title='说明', message='本合成器为进一步提高学团工作智能化程度而设计。\n详细使用方法请看演示视频；相关文件请提前准备好并确认格式正确。\n本合成器主要功能是取代office邮件合并的复杂学习操作，节约人力和时间成本，提高大会筹备效率。\n其中，输出文件文件名将默认为result.docx.')
def show_author():
    showinfo(title='作者', message='上海中学  学代会/团代会证件合成器\n制作人：2601 高煜杰\n本项目在制作完成后将开源给学生会和团委。\n2024.12.28')
def show_update():
    showinfo(title='更新说明', message='V1.0 2024.12.28: \n更新了大部分功能和UI界面。\nV1.1 2025.01.14:\n优化写入路径逻辑，提供exe版程序，修复了部分文件缺失的问题。\nV1.2 2025.01.15:\n新增了选择Excel工作表作为数据源的选项；优化了屏幕拉伸的比例。\n注意：本项目完全开源，如遇报毒请添加信任。如您不信任exe版程序，也可自行配置Python环境后运行源代码。')
def dark_mode():
    style = Style(theme='darkly')
def light_mode():
    style = Style(theme='minty')

menubar = tkinter.Menu(root)
info = tkinter.Menu(menubar, tearoff=0)
menubar.add_cascade(label='关于', menu=info)
info.add_command(label='说明', command=show_info)
info.add_command(label='作者', command=show_author)
info.add_command(label='更新说明', command=show_update)

color = tkinter.Menu(menubar,tearoff=0)
menubar.add_cascade(label='显示主题', menu=color)
color.add_command(label='浅色主题', command=light_mode)
color.add_command(label='深色主题', command=dark_mode)

# 主窗口 #
root.config(menu=menubar) # 展示菜单栏
root.iconbitmap('./favicon.ico') # 窗口角标
root.mainloop() # 主体循环

